unit BlueDiamondUtilV1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
  Vcl.Imaging.pngimage, System.Actions, Vcl.ActnList, Vcl.Touch.GestureMgr,
  CPortCtl, CPort, Vcl.ComCtrls, BD_LabJack, Vcl.Grids, BD_UUT, BD_TestHead,
  Vcl.Menus, ksTimers, IDGlobal;



type
  TfrmMain = class(TForm)
    AppBar: TPanel;
    CloseButton: TImage;
    ComPort1: TComPort;
    sbStatus: TStatusBar;
    Label1: TLabel;
    Memo1: TRichEdit;
    sgADData: TStringGrid;
    rgMode: TRadioGroup;
    sgSN: TStringGrid;
    Label4: TLabel;
    Label3: TLabel;
    tmrADRead: TTimer;
    btnClearData: TButton;
    pnlCal: TPanel;
    btnCaptureZero: TButton;
    btnCaptureMax: TButton;
    btnSendParams: TButton;
    pnlTest: TPanel;
    btnLedOn: TButton;
    btnSegsOn: TButton;
    btnSegsOff: TButton;
    btnLedOff: TButton;
    btnReadAD: TButton;
    btnReadADCont: TButton;
    btnExitCal: TButton;
    btnReadSN: TButton;
    btnSetSN: TButton;
    btnCal: TButton;
    btnReset: TButton;
    Label5: TLabel;
    Label6: TLabel;
    lblADReading: TLabel;
    pnlPressure: TPanel;
    cbFastest: TCheckBox;
    cbSeal: TCheckBox;
    cbVent: TCheckBox;
    cbHighPress: TCheckBox;
    ComFP5: TComPort;
    FP5Term: TComTerminal;
    Label7: TLabel;
    btnAutoCal: TButton;
    Label8: TLabel;
    Button1: TButton;
    MsgTmr: TTimer;
    btnLeakTest: TButton;
    tmrGP: TTimer;
    Panel1: TPanel;
    lblPress: TLabel;
    Label9: TLabel;
    lblLeakRate: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    btnGetPress: TButton;
    pbLeakTime: TProgressBar;
    btnProgram: TButton;
    Label2: TLabel;
    lblDecayVal: TLabel;
    pnlBattery: TPanel;
    Label12: TLabel;
    lblBattV: TLabel;
    Label13: TLabel;
    btnGetBattV: TButton;
    MainMenu1: TMainMenu;
    Settings1: TMenuItem;
    FP5COMSettings1: TMenuItem;
    UUTCOMSettings1: TMenuItem;
    ComTmr: TksTimer;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    //procedure ComPort1RxChar(Sender: TObject; Count: Integer);
    procedure StrToBytes(AStr: string; var rVal: TByteArr);
    procedure CalEnterCal;
    function ScrollRichEditToBottom(var RichEdit: TRichEdit): Boolean;
    procedure btnReadADClick(Sender: TObject);
    procedure rgModeClick(Sender: TObject);
    procedure ResetUUT;
    procedure CalSendParams;
    procedure CalSetSN;
    procedure CalReadSN;
    procedure CalExitCal;
    procedure btnReadADContClick(Sender: TObject);
    procedure tmrADReadTimer(Sender: TObject);
    procedure btnClearDataClick(Sender: TObject);
    procedure CalCaptureZero;
    procedure CalCaptureMax;
    procedure btnSegsOffClick(Sender: TObject);
    procedure btnSegsOnClick(Sender: TObject);
    procedure btnLedOnClick(Sender: TObject);
    procedure btnLedOffClick(Sender: TObject);
    procedure adCaptureMax;
    procedure adCaptureZero;
    procedure adCaptureRtZ;
    procedure btnGetPressClick(Sender: TObject);
    procedure cbFastestClick(Sender: TObject);
    procedure cbVentClick(Sender: TObject);
    procedure cbSealClick(Sender: TObject);
    procedure cbHighPressClick(Sender: TObject);
    procedure btnProgramClick(Sender: TObject);
    procedure CalAutoCal;
    procedure GetUUTPressure;     //Reads the pressure applied to the UUT
    procedure ProcessCalBtns(Sender: TObject);
    procedure ComPort1RxFlag(Sender: TObject);
    procedure SendMsg(Cmd: array of byte; Rply: byte; TimeOut, Cnt: Integer);
    procedure MsgTmrTimer(Sender: TObject);
    procedure CalLeakTest;
    procedure tmrGPTimer(Sender: TObject);
    procedure btnGetBattVClick(Sender: TObject);
    procedure GetBattV;
    procedure FP5COMSettings1Click(Sender: TObject);
    procedure UUTCOMSettings1Click(Sender: TObject);
    procedure ComTmrTimer(Sender: TObject);
    procedure UpdateRx(rx: array of byte; len: Integer);
    procedure rxEnterCal;     //Communicate with UUT to enter calibration- exchange PW.
    procedure rxCaptureAD;  // Obtain A/D data from the UUT via LJ com port.
    procedure rxSendSN;     //Compute and send SN to the UUT via LJ comport.
    procedure rxReceiveSN;   //Request the SN from the UUT and receive via LJ comport.
  private
    { Private declarations }
   
  public
    { Public declarations }

    //ADCnt: Integer;
    //ADArray: Array[0..27] of byte;
    ReadsCounter: Integer;
    AutoCal: Boolean;
  end;

var
  frmMain: TfrmMain;  LJ: TLabJack;  UUT: TUUT; TH: TTestHead;

implementation

{$R *.dfm}


(*******************************************************************************
 btnLedOff: Turn LCD backlight off
*******************************************************************************)
procedure TfrmMain.btnLedOffClick(Sender: TObject);
var
  val: byte;
begin
  val := CAL_LEDOFF;
  Comport1.Write(val, 1);
  Memo1.Lines.Add('Turn LCD backlight off.');
end;

(*******************************************************************************
 btnLedOn: Turn LCD backlight on
*******************************************************************************)
procedure TfrmMain.btnLedOnClick(Sender: TObject);
var
  val: byte;
begin
  val := CAL_LEDON;
  Comport1.Write(val, 1);
  Memo1.Lines.Add('Turn LCD backlight on.');
end;

(*******************************************************************************
 Program: Causes the FP5 Programmer to erase, program, and verify the target
          device.
*******************************************************************************)
procedure TfrmMain.btnProgramClick(Sender: TObject);
begin
  if rgMode.ItemIndex <> 0 then    //ensure in programming mode
    begin
      rgMode.ItemIndex := 0;
      Sleep(200);         //allow relays time to change over
    end;
  ComFP5.WriteStr('ep'+#10+#13);    //complete programming
end;

(*******************************************************************************
 btnSegsOff: Turn all LCD segments off
*******************************************************************************)
procedure TfrmMain.btnSegsOffClick(Sender: TObject);
var
  val: byte;
begin
  val := CAL_LCDOFF;
  Comport1.Write(val, 1);
  Memo1.Lines.Add('Turn all LCD segments off.');
end;
(*******************************************************************************
 btnSegsOn: Turn on all LCD segments.
*******************************************************************************)
procedure TfrmMain.btnSegsOnClick(Sender: TObject);
var
  val: byte;
begin
  val := CAL_LCDON;
  Comport1.Write(val, 1);
  Memo1.Lines.Add('Turn all LCD segments on.');
end;

(*******************************************************************************
btnClearData: Clears all data from the memo
*******************************************************************************)
procedure TfrmMain.btnClearDataClick(Sender: TObject);
begin
  Memo1.Lines.Clear;
end;

(*******************************************************************************
 btnReadADClick: Configures the RX state machine to look for  A to D data from
                 the UUT.
*******************************************************************************)
procedure TfrmMain.btnReadADClick(Sender: TObject);
var
  val: byte; i: Integer;
begin
  ReadsCounter := 1;
  UUT.WaitForAd := True;
  UUT.NumReadsRequired := 1;
  for i := 0 to 27 do
    UUT.ADArrayAvg[i] := 0;
  ComPort1.EventChar := char(CAL_READY);
  ComPort1.TriggersOnRxChar := True;
  val := CAL_SEND_AD;
  ComPort1.Write(val,1);
end;
(*******************************************************************************
 btnReadADClickCont: Enables timer to continuously configures the RX state
                     machine to look for  A to D data from the UUT.
*******************************************************************************)
procedure TfrmMain.btnReadADContClick(Sender: TObject);
const
  {$J+}
  BtnDown: Boolean = False;
  {$J-}
begin
  if BtnDown then
    begin
      BtnDown := False;
      tmrADRead.Enabled := False;
      lblADReading.Visible := False;
    end
    else
      begin
        BtnDown := True;
        tmrADRead.Enabled := True;
      end;
end;


(*******************************************************************************
 btnFastestClick: Toggle Fastest Head
*******************************************************************************)
procedure TfrmMain.cbFastestClick(Sender: TObject);
begin
  cbFastest.Checked := TH.SetFastest(cbFastest.Checked);
end;
(*******************************************************************************
 btnHighPressClick: Toggle High/Low pressure solenoid
*******************************************************************************)
procedure TfrmMain.cbHighPressClick(Sender: TObject);
begin
  cbHighPress.Checked := TH.SetHighLow(cbHighPress.Checked);
end;
                                            
(*******************************************************************************
 btnFastestClick: Toggle Seal solenoid
*******************************************************************************)
procedure TfrmMain.cbSealClick(Sender: TObject);
begin
  cbSeal.Checked := TH.SetSeal(cbSeal.Checked);
end;

(*******************************************************************************
 btnVentClick: Toggle Vent solenoid
*******************************************************************************)
procedure TfrmMain.cbVentClick(Sender: TObject);
begin
  cbVent.Checked := TH.SetVent(cbVent.Checked);
end;
 
(*******************************************************************************
 btnGetPressClick: Read the pressure from the Sensotec and display on GUI
*******************************************************************************)
procedure TfrmMain.btnGetPressClick(Sender: TObject);
begin
  GetUUTPressure;
end;

(*******************************************************************************
 btnGetBattVClick: Read the Battery voltage and display on GUI
*******************************************************************************)
procedure TfrmMain.btnGetBattVClick(Sender: TObject);
begin
  GetBattV;
end;

(*******************************************************************************
 GetUUTPressure: Read the pressure from the Sensotec and Display.
 ******************************************************************************)
procedure TfrmMain.GetUUTPressure;
var
  tmp: Double; i: Integer;
begin
  UUT.AppliedPressure := 0;
  for i := 1 to 10 do
    begin
      LJ.GetAnalogInput(1,tmp);
      UUT.AppliedPressure := UUT.AppliedPressure + tmp;
      Sleep(1);
    end;
  UUT.AppliedPressure := UUT.AppliedPressure/10;      //get average reading
  UUT.AppliedPressure := UUT.AppliedPressure*100;
  lblPress.Caption := FloatToStrF(UUT.AppliedPressure, ffFixed, 15,1) +
                      ' PSI / ' + FloatToStrF(UUT.AppliedPressure/14.69595, ffFixed, 15, 1) +
                      ' ATM';
end;

(*******************************************************************************
 GetBattVClick: Read the Battery voltage and display on GUI. Takes 10 readings,
                calculates average, and displays to two decimal places.
*******************************************************************************)
procedure TfrmMain.GetBattV;
var
  tmp: Double; i: Integer;
begin
  UUT.BattV := 0;
  for i := 1 to 10 do
    begin
      LJ.GetAnalogInput(0,tmp);
      UUT.BattV := UUT.BattV + tmp;
      Sleep(1);
    end;
  UUT.BattV := UUT.BattV/10;      //get average reading
  lblBattV.Visible := True;
  lblBattV.Caption := FloatToStrF(UUT.BattV, ffFixed, 15,2) + ' V';
end;

procedure TfrmMain.MsgTmrTimer(Sender: TObject);
begin
  MsgTmr.Enabled := False;
  ShowMessage('Timer timed out.');

end;

(*******************************************************************************
 FormClose: Performs clean up actions when form is closed (and application is
             exited).
*******************************************************************************)
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  //Turn solenoids off
  cbHighPress.Checked := False;
  cbVent.Checked := False;
  cbSeal.Checked := False;
  cbFastest.Checked := False;
  //Free objects and release mem
  LJ.Free;
  TH.Free;
  UUT.Free;
  LJ.CloseLabJack;
  if ComPort1.Connected then
    ComPort1.Close;
end;

(*******************************************************************************
 FormCreate: Performs initialization tasks when application is first started
             at time that form is created.
*******************************************************************************)
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  LJ := TLabJack.Create;
  TH := TTestHead.Create;
  UUT := TUUT.Create;
  UUT.InitParams;
  try
     LJ.InitLabJack;
  except
    ShowMessage('Error occured while connecting LabJack or opening ComPort.');
  end;
  if FileExists(ExtractFilePath(Application.ExeName)+'UUTComSettings.ini') then
    try
      ComPort1.LoadSettings(stIniFile, ExtractFilePath(Application.ExeName)+'UUTComSettings.ini' );
      ComPort1.Open;
    except
      ShowMessage('Unable to load UUTComSettings.ini. Unable to open port.');
    end;
   if FileExists(ExtractFilePath(Application.ExeName)+'FP5ComSettings.ini') then
    try
      ComFP5.LoadSettings(stIniFile, ExtractFilePath(Application.ExeName)+'FP5ComSettings.ini' );
      ComFP5.Open;
    except
      ShowMessage('Unable to load FP5ComSettings.ini. Unable to open port.');
    end;
  ComPort1.EventChar := #$F0;   //reset ack from UUT
  FillADDataConsts(sgADData);
  FillSNConsts(sgSN);
  //Initialize UUT parameters
  UUT.UUTPendingReset := True;
  UUT.WaitForAD := False;
  UUT.RcvSNFromUUT := False;
  UUT.WaitForSN := False;
  UUT.WaitForCal := False;
  TH.Intialize;
end;

(*******************************************************************************
 FO5COMSettings: Set the COM Port settings for the FP5 Pgmr. User will need
                 to know which port is connected to the Pgmr, using control panel.
******************************************************************************)
procedure TfrmMain.FP5COMSettings1Click(Sender: TObject);
var
  fp: string;
begin
  ShowMessage('FP5 Programmer should be configured for 9600, 8-N-1 '+#13 +
              'on the appropriate COM port.');
  fp := ExtractFilePath(Application.ExeName);
  ComFP5.ShowSetupDialog;
  ComFP5.StoreSettings(stIniFile, fp+'FP5ComSettings.ini');
  if not ComFP5.Connected then
    try
      ComFP5.Open;
    except
      ShowMessage('COM PORT' + ComFP5.Port +' could not be opened.');
    end;
end;

(*******************************************************************************
 rgModeClick: Configure relays to connect to serial port (instead of NEC
              programmer) to allow serial port comms with UUT.
*******************************************************************************)
procedure TfrmMain.rgModeClick(Sender: TObject);
begin
  if rgMode.ItemIndex = 1 then
    begin
      LJ.PutBit(4,1);
      sleep(50);
      LJ.PutBit(5,1);
      sleep(50);
      LJ.PutBit(7,1);
      pnlCal.Enabled := True;
      pnlTest.Enabled := True;
    end
    else
      begin
        LJ.PutBit(4,0);
        sleep(50);
        LJ.PutBit(5,0);
        sleep(50);
        LJ.PutBit(7,0);
        pnlCal.Enabled := False;
        pnlTest.Enabled := False;
    end
end;


(*******************************************************************************
 StrToBytes: Converts a text string of hex digits into an array of bytes.
*******************************************************************************)
procedure TfrmMain.StrToBytes(AStr: string; var rVal: TByteArr);
var
  bVal: TByteArr;
  hNib: byte;
  j, i: Integer;
  IsOdd: Boolean;
begin
  try
      IsOdd := Odd(Length(AStr));
       if IsOdd then
        AStr := '0'+AStr;               //make the tmp an even length (add leading 0 if necessary) so cann for full byte
      SetLength(bVal, Length(AStr));
      SetLength(rVal, Length(bVal) div 2);    //rVal holds the packed byte- half the length of AStr
      for j := 1 to Length(AStr)do
        begin
          if (ord(AStr[j])) <= $39 then        //'9' is 57 in ASCII
           bVal[j-1] := byte(AStr[j]) - $30      //subtract 48 from ASCII numeral equivalents
           else
            bVal[j-1] := byte(AStr[j]) - $37;    //subtract 55 from A-F chars (hex numbers)
         end;
      i := 0;
      j := 0;
      while j <= Length(bVal)-1 do
        begin
          hNib := (bVal[j] shl 4);         //shift hNib to upper 4 bits.
          inc(j);
          rVal[i] := (hNib or bVal[j]);
          inc(j);
          inc(i);
        end;
  except
    ShowMessage('Invalid number.');
  end;
end;
(*******************************************************************************
 tmrADRead: Configures the RX state machine to continousuly look for  A to D
            data from the UUT.
*******************************************************************************)
procedure TfrmMain.tmrADReadTimer(Sender: TObject);
begin
  if lblADReading.Visible = False then
    lblADReading.Visible := True
    else
      lblADReading.Visible := False;
  if not UUT.WaitForAd then
    btnReadAD.Click;
end;

(*******************************************************************************
 tmrGPRead: Timer fires during leak test to measure pressure decay
*******************************************************************************)
procedure TfrmMain.tmrGPTimer(Sender: TObject);
begin
  GetUUTPressure;     //Read the current pressure applied to the UUT
  if (UUT.LeakCnt>1) and (UUT.LeakCnt mod 10 = 0) then   //Step the leak test time progress bar
    pbLeakTime.StepIt;
  lblDecayVal.Caption := 'Decay Val: '+FloatToStrF((UUT.LeakTestStartPress-UUT.AppliedPressure),ffFixed, 15,1) + ' PSI';
  //IF the Leak test exceeds the maximum leak rate...
  if (UUT.LeakTestStartPress - UUT.AppliedPressure) > CAL_LEAK_RATE_MAX  then
    begin
      tmrGP.Enabled := False;
      cbVent.Checked := False;    //Allow to vent
      cbSeal.Checked := False;    //Release seal on syringe
      TH.SetFail(True);
      TH.SetPass(False);
      TH.SetTIP(False);
      ShowMessage('Leak test failure- leak rate:  '+FloatToStrF((UUT.LeakTestStartPress-UUT.AppliedPressure),ffFixed, 15,1) + ' PSI');
      lblLeakRate.Visible := False;
      lblDecayVal.Visible := False;
    end
    else
    //If the leak test  has not failed so far
      begin
        inc(UUT.LeakCnt);
        if UUT.LeakCnt > CAL_LEAK_TIME_TOT then     //If the leak test required time has elapsed.
          begin
            pbLeakTime.Visible := False;
            tmrGP.Enabled := False;
            cbSeal.Checked := False;    //Release seal on syringe
            cbVent.Checked := False;    //Allow to vent
            Memo1.Lines.Add('Leak Rate: ' + FloatToStrF((UUT.LeakTestStartPress-UUT.AppliedPressure),ffFixed, 15,1) + ' PSI');
            if AutoCal then
            begin
              CalEnterCal;
            end
              else
                begin
                  TH.SetFail(False);
                  TH.SetPass(True);
                end;
            lblLeakRate.Visible := False;
            lblDecayVal.Visible := False;
          end;
      end;
end;

(*******************************************************************************
 UUTCOMSettings: Set the COM Port settings for the UUT connection. User will need
                 to know which port is connected to the UUT, using control panel.
******************************************************************************)
procedure TfrmMain.UUTCOMSettings1Click(Sender: TObject);
var
  fp: string;
begin
  ShowMessage('UUT settings should be configured for 9600, 8-N-1 '+#13 +
              'on the appropriate COM port.');
  fp := ExtractFilePath(Application.ExeName);
  ComPort1.ShowSetupDialog;
  ComPort1.StoreSettings(stIniFile, fp+'UUTComSettings.ini');
   if not ComPort1.Connected then
    try
      ComPort1.Open;
    except
      ShowMessage('COM PORT' + ComPort1.Port +' could not be opened.');
    end;
end;

(*******************************************************************************
 ScrollRichEditToBottom: This function auto scrolls the referenced RichEdit
                         when text is added that would cause it to fill the
                         RichEdit to the point that it needs to scroll.
******************************************************************************)
function TfrmMain.ScrollRichEditToBottom(var RichEdit: TRichEdit): Boolean;
var
  l: Integer;
begin
  Result := true;
  l := Length(RichEdit.Text);
  try
    RichEdit.SelStart := l;
    RichEdit.Perform(EM_SCROLLCARET, 0, 0);
  except
    try
      RichEdit.SelStart := l;
      SendMessage(RichEdit.Handle, EM_SCROLLCARET, 0, 0);
    except
      Result := False;
    end;
  end;
end;

(*******************************************************************************
 UpdateRx: Update the rx/tx data memo with information currently in the LJ rx
           buffer.
********************************************************************************)
procedure TfrmMain.UpdateRx(rx: array of byte; len: Integer);
var
 i: Integer; str: string;
begin
  Memo1.Lines.Add('RxCnt: '+IntToStr(len));
  str := 'RX<-';
  for i := 0 to len-1 do    //show the RX Data in the memo.
    str := str + ByteToHex(LJ.RxData[i]) + ' ';
  Memo1.Lines.Add(str);
end;

(*******************************************************************************
 SendMsg(): This procedure sets up the necessary information to exchange data
            between the tester PC and the UUT via RS-232, when the UUT is in
            Calibration mode. Procedure parameters are as follows:
            Cmd: The command to be sent to the UUT. See constants in BD_UUT unit.
            Rply: The expected command acknowldgement. Usually same as Cmd.
            TimeOut: Amount of time to wait for response, to prevent tester hang.
            Cnt: Number of bytes that should be in exchange. See tester spec.
*******************************************************************************)
procedure TfrmMain.SendMsg(Cmd: array of byte; Rply: byte; TimeOut, Cnt: Integer);
var
  len: Integer;
begin
  UUT.RxRplyExpected := Rply;    //update the UUT object expected reply member
  UUT.RxSizeExpected := Cnt;     //update the UUT object receive size expected member
  ComPort1.EventChar := char(Rply);   //update the character that will trigger the comm port received char event
  len := Length(Cmd);            //determine how many bytes will be written to the comm port
  ComPort1.Write(Cmd,len);       //send the cmd
  if TimeOut <> 0 then           //if there is a UUT response time required, then configure the time-out timer accordingly.
    begin
      MsgTmr.Interval := TimeOut;
      MsgTmr.Enabled := True;
    end;
end;

(*******************************************************************************
 ProcessCalBtns: Read the id of the cal panel button that was pressed, and set
                 up the RX state machine accordingly
 ******************************************************************************)
procedure TfrmMain.ProcessCalBtns(Sender: TObject);
begin
  case TButton(Sender).Tag of
    0: CalAutoCal;
    1: ResetUUT;
    2: CalEnterCal;
    3: CalSetSN;
    4: CalReadSN;
    5: begin
         UUT.CaptureZero := True;   //must set here because CalCaptureZero is shared with RTZ capture.
         CalCaptureZero;
       end;
    6: CalCaptureMax;
    7: CalSendParams;
    8: CalExitCal;
    9: begin
         UUT.CaptureRtz := True;
         CalCaptureZero;
       end;
    10: CalLeakTest;
  end;
end;
(*******************************************************************************
 ResetUUT: Issues reset to the UUT.
*******************************************************************************)
procedure TfrmMain.ResetUUT;
begin
  UUT.UUTPendingReset := True;
  ComPort1.EventChar := char(CAL_UUTACTIVE);     //must reset for new UUT not in CAL mode
  (*****Uncomment if using COM port for Reset (development move only)
  ComPort1.SetRTS(True);
  Sleep(20);
  ComPort1.SetRTS(False);
  *****)
  LJ.PutBit(6,0);
  sleep(50);
  LJ.PutBit(6,1);
end;

(*******************************************************************************
 adCaptureMax: find average of 15 readings taken at max pressure.
*******************************************************************************)
procedure TfrmMain.adCaptureMax;
var
  i: Integer;
 begin
   Memo1.Lines.Add('Entered adCaptureMax.');
   for i := 0 to 27 do
     begin
       UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i] div UUT.NumReadsRequired;
       UUT.ADArray[i] := byte(UUT.ADArrayAvg[i]);
     end;
     UUT.GetAvgVals(UUT.MaxVals);
     UUT.CaptureMax := False;
     Memo1.Lines.Add('***********  Max Pressure Averages  *************');
     Memo1.Lines.Add('Current Pressure average: '+ IntToStr(UUT.MaxVals.PresAD));
     Memo1.Lines.Add('Max Pressure average: ' + IntToStr(UUT.MaxVals.PresADMax));
     Memo1.Lines.Add('Min Pressure average: ' + IntToStr(UUT.MaxVals.PresADMin));
     Application.ProcessMessages;
 end;

(*******************************************************************************
 adCaptureZero: find average of 15 readings taken at zero pressure.
*******************************************************************************)
 procedure TfrmMain.adCaptureZero;
var
  i: Integer;
 begin
   Memo1.Lines.Add('Entered adCaptureZero.');
   for i := 0 to 27 do
     begin
       UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i] div UUT.NumReadsRequired;
       UUT.ADArray[i] := byte(UUT.ADArrayAvg[i]);
     end;
     UUT.GetAvgVals(UUT.ZeroVals);
     UUT.CaptureZero := False;
     Memo1.Lines.Add('***********  Zero Pressure Averages  *************');
     Memo1.Lines.Add('Current Pressure average: '+ IntToStr(UUT.ZeroVals.PresAD));
     Memo1.Lines.Add('Max Pressure average: ' + IntToStr(UUT.ZeroVals.PresADMax));
     Memo1.Lines.Add('Min Pressure average: ' + IntToStr(UUT.ZeroVals.PresADMin));
     Application.ProcessMessages;
 end;
 (*******************************************************************************
 adCaptureRtZ: find average of 15 readings taken at zero pressure after having
               been at full pressure.
*******************************************************************************)
 procedure TfrmMain.adCaptureRtZ;
var
  i: Integer;
 begin
   Memo1.Lines.Add('Entered adCaptureRtZ.');
   for i := 0 to 27 do
     begin
       UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i] div UUT.NumReadsRequired;
       UUT.ADArray[i] := byte(UUT.ADArrayAvg[i]);
     end;
     UUT.GetAvgVals(UUT.RTZVals);
     UUT.CaptureRtZ := False;
     Memo1.Lines.Add('***********  Return to Zero Pressure Averages  *************');
     Memo1.Lines.Add('Current Pressure average: '+ IntToStr(UUT.RtZVals.PresAD));
     Memo1.Lines.Add('Max Pressure average: ' + IntToStr(UUT.RtZVals.PresADMax));
     Memo1.Lines.Add('Min Pressure average: ' + IntToStr(UUT.RtZVals.PresADMin));
     Application.ProcessMessages;
 end;

(*******************************************************************************
 CalLeakTest: Perform leak test at 440
*******************************************************************************)
procedure TfrmMain.CalLeakTest;
var
  i: Integer;
begin
 cbFastest.Checked := True;
 cbVent.Checked := True;
 Sleep(50);
 cbHighPress.Checked := True;
 Memo1.Lines.Add('Pressuring UUT to 440 PSI.');
 Application.ProcessMessages;
 Sleep(5000);    //need 5 seconds to pressurize
  i := 0;
  UUT.AppliedPressure := 0;  //sent to initial value to force loop to run.
  while((i<5) and ((UUT.AppliedPressure >441.0) or (UUT.AppliedPressure < 439.0))) do
    begin
      GetUUTPressure;
      Sleep(1000);
      inc(i);
    end;
  if(UUT.AppliedPressure >441.0) or (UUT.AppliedPressure < 439.0) then
    begin
      cbHighPress.Checked := False;   //turn off high pressure
      cbVent.Checked := False;    //Allow to vent
      TH.SetFail(True);
      TH.SetPass(False);
      TH.SetTIP(False);
      ShowMessage('Pressure out of range:  '+FloatToStrF(UUT.AppliedPressure,ffFixed, 15,1));
      Exit;
    end
    else
      //Set up the timer to check pressure over 15 seconds and see if a leak occurs.
      begin
        Memo1.Lines.Add('Syringe successfully pressurized to ' + FloatToStrF(UUT.AppliedPressure,ffFixed, 15,1) + 'PSI.');
        Memo1.Lines.Add('Beginning 15 second leak decay test.');
        cbSeal.Checked := True;   //Seal the pressure in the syring
        cbHighPress.Checked := False; //Turn off High Pressure
        cbVent.Checked := False;  //Release the high pressure from system
        UUT.LeakTestStartPress := UUT.AppliedPressure;    //record starting pressure
        pbLeakTime.Visible := True;
        lblLeakRate.Visible := True;
        lblLeakRate.Caption := FloatToStrF(UUT.AppliedPressure,ffFixed, 15,1);
        lblDecayVal.Visible := True;
        tmrGP.Interval := CAL_LEAK_READ_PER;    //set pressure read interval
        UUT.LeakCnt := 0;     //initialize
        tmrGP.Enabled := True;
      end;
end;

(*******************************************************************************
 CalAutoCal: Run all steps necessary to autocalibrate the syringe
*******************************************************************************)
procedure TfrmMain.CalAutoCal;
begin
  LJ.PutBit(PTIP,1);          //turn on TEST IN PROGRESS lamp
  LJ.PutBit(PPASS,0);         //turn off pass lamp
  LJ.PutBit(PFAIL,0);         //turn off fail lamp
  ComPort1.EventChar := #$F0;     //must reset for new UUT
  sbStatus.Panels[0].Text := 'Executing auto-calibration routine.';
  Memo1.Clear;
  AutoCal := True;
  pnlCal.Enabled := False;  //Disable all buttons until routine is complete.
  //CalEnterCal;     //The RX state machine will take care of the rest.
  CalLeakTest;
end;

(*******************************************************************************
 calEnterCal: Configures RX state machine to look for CAL echo from UUT.
*******************************************************************************)
procedure TfrmMain.calEnterCal;
begin
  UUT.SendCmd(csEnterCal, 5, 10, [$00,$01]);
end;
(*begin
  ComPort1.ClearBuffer(True,True);
  UUT.UUTReady := False;
  UUT.UUTPendingReset := True;
  sbStatus.Panels[0].Text := 'Resetting UUT and entering calibration.';
  Application.ProcessMessages;
  ResetUUT;
  sleep(500);
  UUT.WaitForCal := True;
  UUT.CalPWDone := False;
  SendMsg([$00,$01],CAL_READY,200,2);
end;   *)

(******************************************************************************
 CalSetSN: Set the serial number in the UUT
*******************************************************************************)
procedure TfrmMain.CalSetSN;
begin
  sbStatus.Panels[0].Text := 'Sending SN to UUT.';
  Application.ProcessMessages;
  UUT.SNCnt := 0;
  UUT.WaitForSN := True;
  //Memo1.Lines.Add('SN TX> BD');
  UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [CAL_STORE_SN]);
end;

(*******************************************************************************
 CalReadSN: Reads the serial number back from the UUT
*******************************************************************************)
procedure TfrmMain.CalReadSN;
var
  val: byte;
begin
  (*sbStatus.Panels[0].Text := 'Reading SN from UUT.';
  Application.ProcessMessages;
  UUT.RcvSNFromUUT := True;
  UUT.SNCnt := 0;
  ComPort1.EventChar := (#$AA);
  val := $BB;
  Memo1.Lines.Add('SN RX> BB');
  ComPort1.Write(val,1); *)
  UUT.SendCmd(csReadSN, TIM_SEND_SN, 3, [CAL_SEND_SN]);
end;

(*******************************************************************************
CalCaptureZero: Captures the A/D data measured with zero pressure applied,
                     to be used in A1Calc and C1Calc. Also used to capture
                     the RTZ (Return to Zero) pressure value. 
*******************************************************************************)
procedure TfrmMain.CalCaptureZero;
var
  i: Integer;
begin
  if UUT.CaptureZero then
      sbStatus.Panels[0].Text := 'Capturing Zero pressure values.'
      else if UUT.CaptureRtz then
         sbStatus.Panels[0].Text := 'Capturing Return to Zero pressure values.';
  Application.ProcessMessages;
  cbFastest.Checked := True;
  cbVent.Checked := False;
  cbHighPress.Checked := False;
  Sleep(3000);    //allow pressure to drop
  i := 0;
  UUT.AppliedPressure := 10;  //sent to initial value to force loop to run.
  while((i<5) and (UUT.AppliedPressure <-1.0) or (UUT.AppliedPressure > 1.0)) do
    begin                                       
      GetUUTPressure;
      Sleep(1000);
      inc(i);
    end;
  if(UUT.AppliedPressure <-1.0) or (UUT.AppliedPressure > 1.0) then
    ShowMessage('Pressure out of range:  '+FloatToStrF(UUT.AppliedPressure,ffFixed, 15,3));
  ReadsCounter := 1;
  UUT.WaitForAd := True;
  UUT.NumReadsRequired := 15;
  for i := 0 to 27 do    //clear current values in the array
    UUT.ADArrayAvg[i] := 0;
  UUT.SendCmd(csCaptureAD, TIM_SEND_AD, 5, [CAL_SEND_AD]);
end;

(*******************************************************************************
CalCaptureMax: Captures the A/D data measured with Max pressure applied,
                     to be used in A1Calc and C1Calc
*******************************************************************************)
procedure TfrmMain.CalCaptureMax;
var
  i: Integer;
begin
  sbStatus.Panels[0].Text := 'Capturing Max pressure values.';
  cbFastest.Checked := True;
  cbVent.Checked := True;
  cbHighPress.Checked := True;
  Application.ProcessMessages;
  Sleep(3000);    //need 3 seconds to pressurize
  i := 0;
  UUT.AppliedPressure := 0;  //sent to initial value to force loop to run.
  while((i<5) and ((UUT.AppliedPressure >441.0) or (UUT.AppliedPressure < 439.0))) do
    begin
      GetUUTPressure;
      Sleep(1000);
      inc(i);
    end;
  if(UUT.AppliedPressure >441.0) or (UUT.AppliedPressure < 439.0) then
    ShowMessage('Pressure out of range:  '+FloatToStrF(UUT.AppliedPressure,ffFixed, 15,3));
  //StopCal();
  UUT.PressMeasFullPress := UUT.AppliedPressure;    //save value for calculations
  ReadsCounter := 1;
  UUT.WaitForAd := True;
  UUT.NumReadsRequired := 15;
  UUT.CaptureMax := True;
  for i := 0 to 27 do    //clear current values in the array
    UUT.ADArrayAvg[i] := 0;
  UUT.SendCmd(csCaptureAD, TIM_SEND_AD, 5, [CAL_SEND_AD]);
end;

(*******************************************************************************
 CalSendParams: Send Parameters to the UUT
*******************************************************************************)
procedure TfrmMain.CalSendParams;
begin
  UUT.WaitForSendParams := True;
  UUT.RxIdx := 0;  //init to zero
  sbStatus.Panels[0].Text := 'Saving CAL values to UUT.';
  Application.ProcessMessages;
  UUT.CalcADVals;
  SendMsg([CAL_SEND_PARAMS], CAL_SEND_PARAMS,0, SZ_SEND_PARAMS);
end;

(*******************************************************************************
 CalExitCal: Causes UUT to exit CAL routine.
*******************************************************************************)
procedure TfrmMain.CalExitCal;
begin
  UUT.ExitCal1Done := False;    //initialize
  sbStatus.Panels[0].Text := 'Exiting Calibration Routine.';
  UUT.WaitForExitCal := True;
  SendMsg([CAL_EXIT], CAL_READY, TIM_EXIT, 3);
end;


(******************************************************************************
  ComPort1RxFlag(): This procedure is the heart of the communications state machine
  between the UUT and the PC. Incoming data from the UUT trigger this function
  per the "Rply" parameter of the SendMsg() function, and the data is processed
  accordingly.
*******************************************************************************)
procedure TfrmMain.ComPort1RxFlag(Sender: TObject);
var
  tmp: array of byte;
  str: string;
  Count, i,j,col: Integer;
  //pW: pWord;
  //pB: pByte;
  //val, bVal: byte;
  //msgType: string;
  //accum: Long;
begin
  //First requirment is to see how may bytes were received, and read them into a byte buffer
  //if the received count matches the required count.
  Count := ComPort1.InputCount;
  if (UUT.UUTReady) and (Count = UUT.RxSizeExpected) then
    begin
      MsgTmr.Enabled := False;
      SetLength(tmp, Count);
      ComPort1.Read(tmp[0],Count);      //read num of available bytes in buffer into array
    end
    //Wait for UUT to indicate that it is reset and waiting to activate comms.
  else if UUT.UUTPendingReset then
    begin
      SetLength(tmp, Count);
      ComPort1.Read(tmp[0],Count);      //read num of available bytes in buffer into array
      if tmp[0] = CAL_UUTACTIVE then
      begin
          //MsgTmr.Enabled := False;
          if tmp[high(tmp)] = CAL_READY then
            begin
                  MsgTmr.Enabled := False;
                  UUT.UUTReady := True;
                  UUT.UUTPendingReset := False;
            end;
          end;
              //ComPort1.EventChar := char(CAL_READY);  //set directly here
    end
    //If the count doesn't match, than a coincedental command value may exist as data, triggering
    //the RxFlag, and must be ignored.
    else
      Exit;
//Wait for start CAL
   if UUT.WaitForCal then
       begin
         MsgTmr.Enabled := False;
         case tmp[0] of
           CAL_PW1: begin
                       SendMsg([CAL_PW2], CAL_PW2, TIM_CALPW,1);
                     end;
           CAL_PW2: begin
                       SendMsg([CAL_PW3], CAL_PW3, TIM_CALPW,1);
                     end;
           CAL_PW3: begin
                       SendMsg([CAL_PW4], CAL_READY, TIM_CALPW,2);
                     end;
           CAL_PW4: begin
                       if Count >1 then
                         if tmp[1] = CAL_READY then
                           begin
                             UUT.CalPWDone := True;
                             UUT.WaitForCal := False;
                               if AutoCal then
                                 CalSetSN;
                           end;
                     end;
           CAL_READY: begin
                        if not UUT.CalPWDone then
                           begin
                             SendMsg([CAL_PW1], CAL_PW1, TIM_CALPW,1);
                           end;
                       end;
         end;   //end case
         for i := 0 to Count-1 do
           str :=  str + IntToHex(tmp[i],2)+' ';
         Memo1.Lines.Add('CAL< '+str);
    end

//Wait for A to D data from UUT
  else if UUT.WaitForAd then
    begin
      //First, figure out which column of the A/D data grid the resultant data should go in
      if UUT.CaptureZero then   //0 PSI data stored in column 1 of ADData grid
        col := 1
        else
        if UUT.CaptureMax then  //Max PSI data stored in column 2 of ADData grid
          col := 2
          else
          if UUT.CaptureRtz then   //Return to 0 PSI data stored in column 3 of ADData grid
          col := 3;
      //Now copy the A/D data from the RX buffer into the UUT object A/D data array, assuming CAL_READY was returned
      if (Length(tmp) = UUT.RxSizeExpected) and (tmp[High(tmp)] = CAL_READY) then
        begin
          MsgTmr.Enabled := False;    //Message arrived, stop the timer.
          for i := 1 to 28 do
            UUT.ADArray[i] := tmp[i];
          //Read the data multiple times and take average values, per Test Spec
          if ReadsCounter <= UUT.NumReadsRequired then
            begin
               Memo1.Lines.Add ('AD< '+UUT.ProcessADData(sgADData,col)+
                                'Count: '+IntToStr(ReadsCounter));
               for i := 0 to 27 do    //Add data values to the values in the averaging array
                 UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i]+ UUT.ADArray[i];
               inc(ReadsCounter);     //controls how many reads of A/D data to do
               if ReadsCounter <=UUT.NumReadsRequired then
                 begin
                   SendMsg([CAL_SEND_AD], CAL_READY, TIM_SEND_AD, SZ_AD_VALS);
                 end
                 //Number of required reads are complete. Now process the accumulated data.
                 else
                   begin
                     //Handle capturing of max pressure values average (from 15 reads)
                     if UUT.CaptureMax then
                       begin
                         adCaptureMax;
                         UUT.WaitForAD := False;   //done, set to false
                         if AutoCal then
                           begin
                             UUT.CaptureRtz := True;   //must set this here, as CalCaptureZero is shared with RTZ.
                             CalCaptureZero;
                           end;
                       end
                     //Handle capturing  Zero pressure values average (from 15 reads)
                     else
                     if UUT.CaptureZero then
                       begin
                         adCaptureZero;
                         UUT.WaitForAD := False;   //done, set to false
                         if AutoCal then
                            begin
                              CalCaptureMax;
                            end;
                       end
                     else
                     if UUT.CaptureRtZ then
                       begin
                         adCaptureRtZ;
                         UUT.WaitForAD := False;   //done, set to false
                         if AutoCal then
                            CalSendParams;
                       end;
                   end;

             end;    //end if ReadsCounter < UUT.NumReadsRequired
        end;  //end if Length(tmp)......
     end   //end WaitForAD


//Wait for Send SN to UUT
  else if UUT.WaitForSN then
     begin
       Memo1.Lines.Add('SN<'+IntToHex(byte(tmp[0]),0));
       Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
       //When Send SN is first called, must build SN and prepare to store.
       if (UUT.SNCnt = 0) and (tmp[0] = CAL_STORE_SN)  then
          begin
            MsgTmr.Enabled := False;
            UUT.BuildSN;
            UUT.SNCnt := 1;
            Memo1.Lines.Add(
              'bSN[1]:' + IntToHex(byte(UUT.bSNsent[1]), 0) + #13 +
              'bSN[2]:' + IntToHex(byte(UUT.bSNsent[2]), 0) + #13 +
              'bSN[3]:' + IntToHex(byte(UUT.bSNsent[3]), 0) + #13 +
              'bSN[4]:' + IntToHex(byte(UUT.bSNsent[4]), 0) + #13 +
              'bSN[5]:' + IntToHex(byte(UUT.bSNsent[5]), 0) + #13 +
              'bSN[6]:' + IntToHex(byte(UUT.bSNsent[6]), 0) + #13 +
              'bSN[7]:' + IntToHex(byte(UUT.bSNsent[7]), 0) + #13 +
              'LSB of sum: ' + IntToHex(byte(UUT.bSum), 0)
              );
              SendMsg([UUT.bSNsent[UUT.SNCnt]], UUT.bSNsent[UUT.SNCnt], TIM_SEND_SN, 1);
          end
          else if (tmp[0] = UUT.bSNsent[UUT.SNCnt]) and (UUT.SNCnt <8) then
            begin
              MsgTmr.Enabled := False;
              inc(UUT.SNCnt);
              if UUT.SNCnt <= 7 then
                 begin
                   SendMsg([UUT.bSNsent[UUT.SNCnt]],UUT.bSNsent[UUT.SNCnt],TIM_SEND_SN,1);
                 end
                 else
                   begin
                     SendMsg([UUT.bSum], CAL_READY, TIM_SEND_SN, 2);
                   end;
            end
            else if (UUT.SNCnt > 7) and (tmp[1] = CAL_READY) then
              begin
                 MsgTmr.Enabled :=  False;
                 Memo1.Lines.Add('SN TX< UUT received SN successfully.');
                 Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
                 UUT.WaitForSN := False;
                if AutoCal then
                   CalReadSN;
              end;
   end     //end if WaitForSN

//Wait for receive SN from UUT
   else if UUT.RcvSNFromUUT then
     begin
       if tmp[High(tmp)] = CAL_READY then
       begin
         MsgTmr.Enabled := False;
         for i := 1 to 18 do
           UUT.bSNRcv[i] := tmp[i];
         Memo1.Lines.Add('SN RX< ' + UUT.ProcessSNData(sgSN));
         Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
         UUT.RcvSNFromUUT := False;
         if AutoCal then
            begin
              UUT.CaptureZero := True;  //must set here, as CalCaptureZero is shared with RTZ.
              CalCaptureZero;
            end;
       end;
     end

//Wait for Send Params
  else if UUT.WaitForSendParams then
    begin
      if (tmp[0] = CAL_SEND_PARAMS) and (UUT.RxIdx = 0) then
        begin
          MsgTmr.Enabled := False;    //disable timeout
          UUT.RxIdx := 1;      //load fist element in Parameter array
          SendMsg(UUT.bParams[UUT.RxIdx], UUT.bParams[UUT.RxIdx], TIM_SEND_PARAMS, SZ_SEND_PARAMS);   //start the msg cycle to send params
        end
      //Now cycle through the 47 parameters, eschanging and verifying a byte at at time. UUT wil echo each byte.
      else if (tmp[0] = UUT.RxRplyExpected) and (UUT.RxIdx < SZ_TOTAL_PARAMS) then
        begin
          MsgTmr.Enabled := False; //disable timeout
          inc(UUT.RxIdx);
          if UUT.RxIdx < SZ_TOTAL_PARAMS then
            SendMsg(UUT.bParams[UUT.RxIdx], UUT.bParams[UUT.RxIdx], TIM_SEND_PARAMS, SZ_SEND_PARAMS)
            else
              SendMsg(UUT.bParams[UUT.RxIdx], CAL_READY, 0, 2)  //should receive final param echo plus READY (2 bytes).
        end
      //47 paramater bytes have been sent, make sure a CAL_READY byte was returned from the UUT.
      else if (UUT.RxIdx >= SZ_SEND_PARAMS) and (Length(tmp) > 1) then
        if tmp[1] = CAL_READY then
          begin
            MsgTmr.Enabled := False; //disable timout
            UUT.WaitForSendParams := False;
            sbStatus.Panels[0].Text := 'Parameters successfully stored.';
            Memo1.Lines.Add('STOR PARAMS< Parameters Stored.');
            Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
            if AutoCal then
               begin
                 CalExitCal;
                 pnlCal.Enabled := True;
               end;
            Application.ProcessMessages;
          end;
    end
//Wait for ExitCal
   else if UUT.WaitForExitCal then
     begin
       if (tmp[1] = CAL_READY) and (tmp[2] = CAL_READY) then
         begin
           LJ.PutBit(PPASS,1);
           Memo1.Lines.Add('Exit Cal<  Cal complete and exiting.');
           Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
           sbStatus.Panels[0].Text := 'Calibration complete.';
         end
         else
           begin
              LJ.PutBit(PFAIL,1);
              Memo1.Lines.Add('Exit Cal<  Cal complete and exiting.');
              Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
              sbStatus.Panels[0].Text := 'Calibration complete.';
           end;
         LJ.PutBit(PTIP,0);    //Turn off TEST IN PROGRESS lamp
         MsgTmr.Enabled := False;
         AutoCal := False;
         UUT.WaitForExitCal := False;
     end

//Char received with no "wait" flags set
   else
     begin
       for i := 0 to Count-1 do
         str :=  str + IntToHex(tmp[i],2)+' ';
         Memo1.Lines.Add('Other< '+ str);
         Memo1.SetFocus;
      end;
    Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
    Application.ProcessMessages;
end;


(*******************************************************************************
 ComTmrTimer: This timer fires after the amountof time specified by the respTime
              parameter of the SendCmd function. It then calls the appropriate
              procedure based on the UUT.CalSequence value.
*******************************************************************************)
procedure TfrmMain.ComTmrTimer(Sender: TObject);
begin
  //make sure the LJ rx buffer hasn't been read too many times (timed out)
  inc(LJ.RxRespCnt);
    if LJ.RxRespCnt > LJ.RxRespTries then
      begin
        ComTmr.Enabled := False;
        ShowMessage('Timer timed out.');
        LJ.RxRespCnt := 0;
      end
  //Now check the LJ rx buffer to see if the UUT has responded.
  else
  begin
    case UUT.CalSequence of
      csEnterCal: rxEnterCal;
      csCaptureAD: rxCaptureAD;
      csSendSN: rxSendSN;
      csReadSN: rxReceiveSN;
    end;
  end;
end;

(*******************************************************************************
 rxEnterCal: Command UUT to enter cal mode. Exchange PW with UUT.
*******************************************************************************)
procedure TfrmMain.rxEnterCal;
var
  i: Integer; str: string; iRxCnt: Integer;
begin
  LJ.ReadUART;
  iRxCnt := Round(LJ.RxCnt);
  UpdateRx(LJ.RxData, iRxCnt);
  if iRxCnt > 0 then
    begin
      ComTmr.Enabled := False;    //kill the timer
      LJ.RxRespCnt := 0;          //zero the counter.
      if ((LJ.RxData[0] = CAL_UUTACTIVE) and (LJ.RxData[iRxCnt-1] = CAL_READY)) then
        UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW1])
      else if LJ.RxData[iRxCnt-1] = CAL_PW1 then
        UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW2])
      else if LJ.RxData[iRxCnt-1] = CAL_PW2 then
        UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW3])
      else if LJ.RxData[iRxCnt-1] = CAL_PW3 then
        UUT.SendCmd(csEnterCal, 5, 10, [CAL_PW4])
      else if LJ.RxData[0] = CAL_PW4 then
        begin
          if iRxCnt > 1 then
            if LJ.RxData[iRxCnt-1] = CAL_READY then
              Memo1.Lines.Add('Calibration mode entered successfully!')
              else
                Memo1.Lines.Add('Failed to enter Calibration mode.');
        end;
    end;
end;

(*******************************************************************************
 rxCaptureAD: Obtain A/D data from the UUT via LJ com port.
*******************************************************************************)
procedure TfrmMain.rxCaptureAD;
var
  col, i: Integer; str: string; iRxCnt: Integer;
begin
  LJ.ReadUART;
  iRxCnt := Round(LJ.RxCnt);
  UpdateRx(LJ.RxData, iRxCnt);
  if iRxCnt > 0 then
    begin
      //Now copy the A/D data from the RX buffer into the UUT object A/D data array, assuming CAL_READY was returned
      if (iRxCnt = SZ_AD_VALS) and (LJ.RxData[iRxCnt-1] = CAL_READY) then
        begin
          ComTmr.Enabled := False;    //kill the timer
          LJ.RxRespCnt := 0;
          //Figure out which column of the A/D data grid the resultant data should go in
          if UUT.CaptureZero then   //0 PSI data stored in column 1 of ADData grid
            col := 1
            else
            if UUT.CaptureMax then  //Max PSI data stored in column 2 of ADData grid
              col := 2
              else
              if UUT.CaptureRtz then   //Return to 0 PSI data stored in column 3 of ADData grid
              col := 3;
          for i := 1 to 28 do
            UUT.ADArray[i] := LJ.RxData[i];
          //Read the data multiple times and take average values, per Test Spec
          if ReadsCounter <= UUT.NumReadsRequired then
            begin
               Memo1.Lines.Add ('AD< '+UUT.ProcessADData(sgADData,col)+
                                'Count: '+IntToStr(ReadsCounter));
               for i := 0 to 27 do    //Add data values to the values in the averaging array
                 UUT.ADArrayAvg[i] := UUT.ADArrayAvg[i]+ UUT.ADArray[i];
               inc(ReadsCounter);     //controls how many reads of A/D data to do
               if ReadsCounter <=UUT.NumReadsRequired then
                 begin
                   UUT.SendCmd(csCaptureAD, TIM_SEND_AD, 5, [CAL_SEND_AD]);
                 end   //Request next read and exit the routine.
                 //Number of required reads are complete. Now process the accumulated data.
                 else
                   begin
                     //Handle capturing of max pressure values average (from 15 reads)
                     if UUT.CaptureMax then
                       begin
                         adCaptureMax;
                         UUT.WaitForAD := False;   //done, set to false
                         if AutoCal then
                           begin
                             UUT.CaptureRtz := True;   //must set this here, as CalCaptureZero is shared with RTZ.
                             CalCaptureZero;
                           end;
                       end
                     //Handle capturing  Zero pressure values average (from 15 reads)
                     else
                     if UUT.CaptureZero then
                       begin
                         adCaptureZero;
                         UUT.WaitForAD := False;   //done, set to false
                         if AutoCal then
                            begin
                              CalCaptureMax;
                            end;
                       end
                     else
                     if UUT.CaptureRtZ then
                       begin
                         adCaptureRtZ;
                         UUT.WaitForAD := False;   //done, set to false
                         if AutoCal then
                            CalSendParams;
                       end;
                   end;

             end;    //end if ReadsCounter < UUT.NumReadsRequired
        end  //end if Length(tmp)......
        else
          Memo1.Lines.Add('Attempt # '+ IntToStr(LJ.RxRespCnt));
     end;    //end if RxCnt > 0
end;
(*******************************************************************************
 rxSendSN: Compute SN and send to UUT via LJ comport.
*******************************************************************************)
procedure TfrmMain.rxSendSN;
var
  col, i: Integer; str: string; iRxCnt: Integer;
begin
  LJ.ReadUART;
  iRxCnt := Round(LJ.RxCnt);
  UpdateRx(LJ.RxData, iRxCnt);
  if iRxCnt>0 then
  begin
    ComTmr.Enabled := False;
    LJ.RxRespCnt := 0;
    //When Send SN is first called, must build SN and prepare to store.
    if (UUT.SNCnt = 0) and (LJ.RxData[0] = CAL_STORE_SN)  then
      begin
        UUT.BuildSN;
        UUT.SNCnt := 1;
        Memo1.Lines.Add(
          'bSN[1]:' + IntToHex(byte(UUT.bSNsent[1]), 0) + #13 +
          'bSN[2]:' + IntToHex(byte(UUT.bSNsent[2]), 0) + #13 +
          'bSN[3]:' + IntToHex(byte(UUT.bSNsent[3]), 0) + #13 +
          'bSN[4]:' + IntToHex(byte(UUT.bSNsent[4]), 0) + #13 +
          'bSN[5]:' + IntToHex(byte(UUT.bSNsent[5]), 0) + #13 +
          'bSN[6]:' + IntToHex(byte(UUT.bSNsent[6]), 0) + #13 +
          'bSN[7]:' + IntToHex(byte(UUT.bSNsent[7]), 0) + #13 +
          'LSB of sum: ' + IntToHex(byte(UUT.bSum), 0)
          );
          UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [UUT.bSNsent[UUT.SNCnt]]);
      end
      else if (LJ.RxData[0] = UUT.bSNsent[UUT.SNCnt]) and (UUT.SNCnt <8) then
        begin
          inc(UUT.SNCnt);
          if UUT.SNCnt <= 7 then
             begin
               UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [UUT.bSNsent[UUT.SNCnt]]);
             end
             else
               begin
                 UUT.SendCmd(csSendSN, TIM_STORE_SN, 3, [UUT.bSNsent[UUT.bSum]]);
               end;
        end
        else if (UUT.SNCnt > 7) and (LJ.RxData[1] = CAL_READY) then
          begin
             Memo1.Lines.Add('SN TX< UUT received SN successfully.');
             Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
             UUT.WaitForSN := False;
            if AutoCal then
               CalReadSN;
          end;
   end;   //end if iRxCnt > 0
 end;

 (*******************************************************************************
 rxRecieveSN: Compute SN and send to UUT via LJ comport.
*******************************************************************************)
procedure TfrmMain.rxReceiveSN;
var
  col, i: Integer; str: string; iRxCnt: Integer;
begin
  LJ.ReadUART;
  iRxCnt := Round(LJ.RxCnt);
  UpdateRx(LJ.RxData, iRxCnt);
  if iRxCnt>0 then
     begin
        ComTmr.Enabled := False;
       LJ.RxRespCnt := 0;
       if LJ.RxData[iRxCnt-1] = CAL_READY then
       begin
         for i := 1 to 18 do
           UUT.bSNRcv[i] := LJ.RxData[i];
         Memo1.Lines.Add('SN RX< ' + UUT.ProcessSNData(sgSN));
         Memo1.Perform(EM_SCROLL,SB_LINEDOWN,0);
         UUT.RcvSNFromUUT := False;
         if AutoCal then
            begin
              UUT.CaptureZero := True;  //must set here, as CalCaptureZero is shared with RTZ.
              CalCaptureZero;
            end;
       end;
     end;  //end if RxCnt>0
end;
end.
